Merge from hashar's branch.
== Major new features ==
+$wgAjaxShowEditors will let your users see who else is editing the page
+they are currently editing.
+
== Changes since 1.9 ==
* (bug 7292) Fix site statistics when moving pages in/out of content namespaces
return $watch ? '<w#>' : '<u#>';
}
+
+/**
+ * Return a list of Editors currently editing the article.
+ * Based on an idea by Tim Starling.
+ *
+ * @author Ashar Voultoiz <hashar@altern.org>
+ * @author Tim Starling
+ */
+function wfAjaxShowEditors( $articleId, $username ) {
+ global $wgOut;
+ $articleId = intval($articleId);
+
+ // Validate request
+ $title = Title::newFromID( $articleId );
+ if( !($title) ) { return 'ERR: page id invalid'; }
+
+ $user = User::newFromSession() ;
+ if( !$user ) { return 'ERR: user invalid'; }
+
+ $username = $user->getName();
+ if( !( $user->isLoggedIn() or User::isIP( $username ) ) ) { return 'ERR: user not found'; }
+
+
+ // When did the user started editing ?
+ $dbr =& wfGetDB(DB_SLAVE);
+ $userStarted = $dbr->selectField( 'editings',
+ 'editings_started',
+ array(
+ 'editings_user' => $username,
+ 'editings_page' => $title->getArticleID(),
+ ),
+ __METHOD__
+ );
+
+ // He just started editing, assume NOW
+ if(!$userStarted) { $userStarted = $dbr->timestamp(); }
+
+ # Either create a new entry or update the touched timestamp.
+ # This is done using a unique index on the database :
+ # `editings_page_started` (`editings_page`,`editings_user`,`editings_started`)
+
+ $dbw =& wfGetDB(DB_MASTER);
+ $dbw->replace( 'editings',
+ array( 'editings_page', 'editings_user', 'editings_started' ),
+ array(
+ 'editings_page' => $title->getArticleID() ,
+ 'editings_user' => $username,
+ 'editings_started' => $userStarted ,
+ 'editings_touched' => $dbw->timestamp(),
+ ), __METHOD__
+ );
+
+ // Now we get the list of all watching users
+ $dbr = & wfGetDB(DB_SLAVE);
+ $res = $dbr->select( 'editings',
+ array( 'editings_user','editings_started','editings_touched' ),
+ array( 'editings_page' => $title->getArticleID() ),
+ __METHOD__
+ );
+
+ $l = new Linker();
+
+ $wikitext = '';
+ $unix_now = wfTimestamp(TS_UNIX);
+ $first = 1;
+ while( $editor = $dbr->fetchObject( $res ) ) {
+
+ // Check idling time
+ $idle = $unix_now - wfTimestamp( TS_UNIX, $editor->editings_touched );
+
+ global $wgAjaxShowEditorsTimeout ;
+ if( $idle >= $wgAjaxShowEditorsTimeout ) {
+ $dbw->delete('editings',
+ array(
+ 'editings_page' => $title->getArticleID(),
+ 'editings_user' => $editor->editings_user,
+ ),
+ __METHOD__
+ );
+ continue; // we will not show the user
+ }
+
+ if( $first ) { $first = 0; }
+ else { $wikitext .= ' ~ '; }
+
+ $since = wfTimestamp( TS_DB, $editor->editings_started );
+ $wikitext .= $since;
+
+ $wikitext .= ' ' . $l->makeLinkObj(
+ Title::makeTitle( NS_USER, $editor->editings_user ),
+ $editor->editings_user
+ );
+
+
+ $wikitext .= ' ' . wfMsg( 'ajax-se-idling', '<span>'.$idle.'</span>' );
+ }
+ return $wikitext ;
+}
?>
--- /dev/null
+<?php
+if( !defined( 'MEDIAWIKI' ) )
+ die( 1 );
+
+/**
+ $article: the article (object) saved
+ $user: the user (object) who saved the article
+ $text: the new article text
+ $summary: the article summary (comment)
+ $isminor: minor flag
+ $iswatch: watch flag
+ $section: section #
+*/
+function wfAjaxShowEditorsCleanup( $article, $user ) {
+ $articleId = $article->getID();
+ $userId = $user->getName();
+
+ $dbw =& wfGetDB(DB_MASTER);
+ $dbw->delete('editings',
+ array(
+ 'editings_page' => $articleId,
+ 'editings_user' => $userId,
+ ),
+ __METHOD__
+ );
+}
+
+$wgHooks['ArticleSaveComplete'][] = 'wfAjaxShowEditorsCleanup';
+?>
* to ensure that client-side caches don't keep obsolete copies of global
* styles.
*/
-$wgStyleVersion = '51';
+$wgStyleVersion = '52';
# Server-side caching:
*/
$wgAjaxWatch = false;
+/**
+ * Let you show other peoples editing an article.
+ */
+$wgAjaxShowEditors = false;
+/**
+ * Number of seconds before an user is considered as no more editing
+ */
+$wgAjaxShowEditorsTimeout = 60;
+
/**
* Allow DISPLAYTITLE to change title display
*/
$templates = ($this->preview || $this->section) ? $this->mPreviewTemplates : $this->mArticle->getUsedTemplates();
$formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != '');
+
+ global $wgAjaxShowEditors ;
+ if ( $wgAjaxShowEditors && isset( $this->mArticle )
+ && isset( $this->mArticle->mRevision ) ) {
+ global $wgJsMimeType, $wgStylePath, $wgStyleVersion;
+ $wgOut->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxshoweditors.js?$wgStyleVersion\"></script>\n" );
+ $wgOut->addWikiText(
+ '<div id="ajax-se"><p id="ajax-se-title">'.wfMsg('ajax-se-title').'</p>'
+ . '<p id="ajax-se-editors">'. wfMsg('ajax-se-pending') . '</p>'
+ . '</div>'
+ );
+ }
+
global $wgUseMetadataEdit ;
if ( $wgUseMetadataEdit ) {
$metadata = $this->mMetaData ;
if ( $wgAjaxSearch ) $wgAjaxExportList[] = 'wfSajaxSearch';
if ( $wgAjaxWatch ) $wgAjaxExportList[] = 'wfAjaxWatch';
+if ( $wgAjaxShowEditors ) $wgAjaxExportList[] = 'wfAjaxShowEditors';
wfSeedRandom();
#
# Send Ajax requests to the Ajax dispatcher.
#
-if ( $wgUseAjax && $action == 'ajax' ) {
- require_once( $IP . '/includes/AjaxDispatcher.php' );
-
- $dispatcher = new AjaxDispatcher();
- $dispatcher->performAction();
- $mediaWiki->restInPeace( $wgLoadBalancer );
- exit;
+if ( $wgUseAjax ) {
+ if( $action == 'ajax' ) {
+ require_once( $IP . '/includes/AjaxDispatcher.php' );
+
+ $dispatcher = new AjaxDispatcher();
+ $dispatcher->performAction();
+ $mediaWiki->restInPeace( $wgLoadBalancer );
+ exit;
+ } else {
+ require_once( $IP . '/includes/AjaxHooks.php' );
+ }
}
'size-megabytes' => '$1 MB',
'size-gigabytes' => '$1 GB',
+# Ajax show editors
+'ajax-se-title' => 'Currently editing:',
+'ajax-se-pending' => 'pending refresh ... (click this box or start editing)',
+'ajax-se-idling' => '($1s ago)',
+
);
?>
--- /dev/null
+--
+-- Tracks people currently editing an article
+-- Enabled with $wgAjaxShowEditors = true;
+--
+
+CREATE TABLE /*$wgDBprefix*/editings (
+ `editings_page` int(8) NOT NULL,
+ `editings_user` varchar(255) NOT NULL,
+ `editings_started` char(14) NOT NULL,
+ `editings_touched` char(14) NOT NULL,
+ PRIMARY KEY (`editings_page`,`editings_user`),
+ KEY `editings_page` (`editings_page`)
+ KEY `editings_page_started` (`editings_page`,`editings_user`,`editings_started`)
+) TYPE=InnoDB;
array( 'transcache', 'patch-transcache.sql' ),
array( 'trackbacks', 'patch-trackbacks.sql' ),
array( 'externallinks', 'patch-externallinks.sql' ),
+ array( 'editings', 'patch-editings.sql' ),
array( 'job', 'patch-job.sql' ),
array( 'langlinks', 'patch-langlinks.sql' ),
array( 'querycache_info', 'patch-querycacheinfo.sql' ),
--- /dev/null
+var sajax_debug_mode = false;
+var canRefresh = null;
+var ShowEditorsCounting = false;
+var wgAjaxShowEditors = {} ;
+
+// The loader. Look at bottom for the sajax hook registration
+wgAjaxShowEditors.onLoad = function() {
+ var elEditors = document.getElementById( 'ajax-se' );
+ // wgAjaxShowEditors.refresh();
+ elEditors.onclick = function() { wgAjaxShowEditors.refresh(); } ;
+
+ var elTextArea = document.getElementById( 'wpTextbox1' );
+ elTextArea.onkeypress = function() { wgAjaxShowEditors.refresh(); } ;
+
+ wgAjaxShowEditors.allowRefresh();
+}
+
+
+// Ask for new data & update UI
+wgAjaxShowEditors.refresh = function() {
+ if( !canRefresh ) { return; }
+
+ // Disable new requests for 5 seconds
+ canRefresh = false;
+ setTimeout( 'wgAjaxShowEditors.allowRefresh()', 5000 );
+
+ // Load the editors list element, it will get rewrote
+ var elEditorsList = document.getElementById( 'ajax-se-editors' );
+
+ if( wgUserName == null ) {
+ wgUserName = '';
+ }
+
+ // Do the ajax call to the server
+ sajax_do_call( "wfAjaxShowEditors", [ wgArticleId, wgUserName ], elEditorsList );
+ if(!ShowEditorsCounting) {
+ wgAjaxShowEditors.countup();
+ }
+}
+
+wgAjaxShowEditors.countup = function() {
+ ShowEditorsCounting = true;
+
+ var elEditorsList = document.getElementById( 'ajax-se-editors' );
+ for(var i=0;i<elEditorsList.childNodes.length;i++) {
+ var item = elEditorsList.childNodes[i];
+ if (item.nodeName == 'SPAN') {
+ var value = parseInt( item.innerHTML );
+ value++;
+ item.innerHTML = value ;
+ }
+ }
+ setTimeout( "wgAjaxShowEditors.countup()", 1000 );
+}
+
+// callback to allow refresh
+wgAjaxShowEditors.allowRefresh = function() {
+ canRefresh = true;
+}
+
+// Register our initialization function.
+hookEvent( "load", wgAjaxShowEditors.onLoad);
var supportsAjax = request ? true : false;
delete request;
return supportsAjax;
-}
\ No newline at end of file
+}
}
tr.sv-space td { display: none; }
+
+/* wgAjaxShowEditors */
+
+#ajax-se {
+ border:1px solid #aaaaaa;
+ margin: 0 0 1em 0;
+ padding:0.15em;
+ color : #000000;
+ background-color: #F0F0F0;
+}
+
+#ajax-se-title {
+ font-size:small;
+ display:inline;
+ margin-right:1em;
+}
+
+#ajax-se-editors {
+ display:inline;
+}
+
/*
Table pager (e.g. Special:Imagelist)
- remove underlines from the navigation link
*/
#toolbar { clear: both; }
.mw-plusminus-null { color: #aaa; }
-.texvc { direction: ltr; unicode-bidi: embed; }
\ No newline at end of file
+.texvc { direction: ltr; unicode-bidi: embed; }